/**
 * Copyright (c) 2009 BadrIT - Badr for Information Technology
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 **/ 
package com.badrit.restarm
{
	import flash.utils.Dictionary;
	
	import mx.collections.ArrayCollection;
	import mx.collections.ListCollectionView;
	import mx.controls.Alert;
	import mx.events.CollectionEvent;
	import mx.events.CollectionEventKind;
	import mx.events.PropertyChangeEvent;
	import mx.rpc.AsyncToken;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.mxml.HTTPService;
	
	/**
	 * Class that provides Restfull actions (Create\Edit\List\Delete) to a Restfull back end  
	 */	
	public class RESTfulActiveResourceManager
	{
		/**
		 * Whether new resources are automatically saved to database or not.
		 * The default is false, so the commit should be done by the user to save 
		 * the resources chages to database.
		 * 
		 * @default false
		 */
		public var autoSave:Boolean = false;
		
		/**
		 * Whether this resource needs to be sorted or not.
		 * This requires the backend to implement the sort action which accepts
		 * a comma-separated list of ids to sort.
		 * 
		 * @default false
		 **/
		public var sortable:Boolean = false;
		
		/**
		 * A callback that need to be called after creation of each resource
		 */		
		public var afterCreateHandler:Function = null;
		
		/**
		 * The base URL under which this resource is located. This must be ending in slash
		 * i.e, http://localhost:3000/
		 */		
		private var _baseURL:String;
		
		/**
		 * The name of the resource (the plural one), i.e, <books>
		 */		
		private var _resourceName:String;
		
		/**
		 * The single of the resource name.
		 * This is used to parse XML and to send the correct parameters
		 */		
		private var _resourceSingleName:String;
		
		/**
		 * The HTTPService used to send all requests to the server
		 */		
		private var _httpService:HTTPService;
		
		/**
		 * The local collection of resources to which this resource manager is listening
		 * i.e, the collection which will contain the comming resources for listing.
		 */		
		private var _collection:ListCollectionView;
		
		/**
		 * The converter which converts local objects to remote and vice-versa 
		 */		
		private var _converter:IRESTfulActiveResource;
		
		/**
		 * Maps each AsyncToken to the required ResultHandler used with this particular request
		 * Used handlers are automatically removed after they are called
		 **/
		private var _defaultResultHandlers:Dictionary;
		
		/**
		 * Maps each AsyncToken to the required FaultHandler used with this particulr request
		 * Used handlers are automatically removed after they are called
		 **/
		private var _defaultFaultHandlers:Dictionary;
		
		/**
		 * Maps each AsyncToken to the object that was used with this request.
		 * This is useful when we need to set the id of the newly created objects after
		 * the result is returned from the server.
		 * When request is returned, it automatically removes the token and object from this map
		 **/
		private var _token2Object:Dictionary;
		
		/**
		 * Whether the current state of resources are sorted or we
		 * need to call the backend to sort them
		 **/		
		private var _needSorting:Boolean;

		/**
		 * A list of handlers that want to be called whenever this collection is refreshed.
		 **/		
		private var _notifyListLoaded:ArrayCollection;
		
		/**
		 * A list of handlers that want to be called whenever this collection is failed to refresh
		 **/
		private var _notifyListFailed:ArrayCollection;
		
		/**
		 * A list of newly created objects that are not saved to backend yet
		 */		
		private var _objects2bCreated:ArrayCollection;
		
		/**
		 * A list of updated objects that need to be updated at the backend
		 */		
		private var _objects2bUpdated:ArrayCollection;
		
		/**
		 * A list of deleted objects that need to be deleted at the backend
		 */		
		private var _objects2bDeleted:ArrayCollection;
		
		/**
		 * A list of objects that have been sent to the server for creation but not finished yet
		 */		
		private var _objectsBeingCreated:ArrayCollection;
		
		/**
		 * A list of objects that have been sent to the server for updating but not finished yet
		 */		
		private var _objectsBeingUpdated:ArrayCollection;
		
		/**
		 * A list of objects that have been sent to the server for deletion but not finished yet
		 */		
		private var _objectsBeingDeleted:ArrayCollection;
	
		/**
		 * Whether there's a commit in progress or not. Used to inhibit two simultaneous commits
		 */		 
		private var _commitInProgress:Boolean;
		
		/**
		 * A set of functions to be called when the current commit finishes
		 */		
		private var _commitCallback:ArrayCollection;

		
		
		/**
		 * Creates a new REST client
		 **/
		public function RESTfulActiveResourceManager(){
		 	_httpService = new HTTPService();
		 	_httpService.resultFormat = "object";
		 	_httpService.useProxy = false;
		 	_httpService.requestTimeout = 30;
		 	_httpService.addEventListener(ResultEvent.RESULT, fireResultHandler);
		 	_defaultResultHandlers = new Dictionary();
		 	_httpService.addEventListener(FaultEvent.FAULT, fireFaultHandler);
		 	_defaultFaultHandlers = new Dictionary();
		 	_token2Object = new Dictionary();
			_httpService.showBusyCursor = false;
			_notifyListLoaded = new ArrayCollection();
			_notifyListFailed = new ArrayCollection();
			_objects2bCreated = new ArrayCollection();
			_objects2bUpdated = new ArrayCollection();
			_objects2bDeleted = new ArrayCollection();
			_objectsBeingCreated = new ArrayCollection();
			_objectsBeingUpdated = new ArrayCollection();
			_objectsBeingDeleted = new ArrayCollection();
			_commitCallback = new ArrayCollection();
		}

		/**
		 * Attaches this resource manager to a collection of items.
		 * This collection of items is updated elsewhere in the code.
		 * This ActiveResourceManager should listen to changes to this collection and
		 * updates the BackEnd when this collection changes.
		 **/
		public function attachToCollection(collection:ListCollectionView, converter:IRESTfulActiveResource,
			resourceName:String, autoFill:Boolean = true):void{
			collection.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChanged);
			_collection = collection;
			_converter = converter;
		 	_resourceName =  resourceName;
		 	_resourceSingleName = resourceName.substr(0, resourceName.length -1 );
		 	if (autoFill)
		 		refreshList();
		}
		
		/**
		 * Refresh the list from database and update it to the collection attached
		 **/
		public function refreshList(loadedCallback:Function=null, failedCallback:Function=null):void {
			if (loadedCallback != null)
				addListLoadedHandler(loadedCallback);
				
			if (failedCallback != null)
				addListFailedHandler(failedCallback);
				
	 		list(handleList, handleListFailure);
		}
		
		/**
		 * Commit all changes to the database
		 **/
		public function commit(callback:Function = null):void {
			// Even if there's a commit in progress we will
			if (callback != null) 
				_commitCallback.addItem(callback);
			//do not allow simultaneous commit actions
			if (_commitInProgress)
				return;
			_commitInProgress = true;
			//TODO limit the number of connections to the server
			//handle objects to be inserted
			for each (var obj:Object in _objects2bCreated) {
				var remoteObject:Object = _converter.localToRemote(obj);
				//store this object to fill its id when it is created
				_token2Object[createItem(remoteObject, handleObjectCreated)] = obj;
				// Move them to objectsBeingCreated where they will be ...
				// ... removed one by one as it is saved to server. (done in handleObjectCreated)
				_objectsBeingCreated.addItem(obj);
			}
			// No more objects will be created for now
			_objects2bCreated.removeAll();
			
			//handle objects to be deleted
			for each (obj in _objects2bDeleted) {
				//store this object to remove it from list upon deletion
				_token2Object[deleteItem(obj.id, handleObjectDeleted)] = obj;
				// Move them to objectsBeingDeleted
				_objectsBeingDeleted.addItem(obj);
			}
			// No more objects will be deleted for now
			_objects2bDeleted.removeAll();
			
			//handle objects to be updated
			for each (obj in _objects2bUpdated) {
				//store this object to remove it from list upon update
				_token2Object[update(obj, handleObjectUpdated)] = obj;
				
				// Move them to objectsBeingUpdated
				_objectsBeingUpdated.addItem(obj);
			}
			// No more objects are set to be updated
			_objects2bUpdated.removeAll();

			//this check is useful when commit really does nothing
			checkCommitComplete();
		}
		
		/**
		 * Gets the detailed information of one resource from the server
		 * URL: <base_url>/<resource>s/id.xml
		 * METHOD: get
		 **/
		public function show(id:Number, resultHandler:Function=null, faultHandler:Function=null):AsyncToken {
			_httpService.url = _baseURL+_resourceName+"/"+id+".xml";
			_httpService.method = "get"
			var token:AsyncToken = _httpService.send();
			if (resultHandler != null)
				_defaultResultHandlers[token] = resultHandler;
			if (faultHandler != null)
				_defaultFaultHandlers[token] = faultHandler;
			return token;
		}
		
		/**
		 * Add a handler to the list of list loaded handlers.
		 * Functions in this list will be notified when the list of objects
		 * is loaded completely from server.
		 * This may be useful when there are other resources that depend on
		 * instances of the resources attached to this manager.
		 **/
		public function addListLoadedHandler(handler:Function):void{
			if (!_notifyListLoaded.contains(handler))
				_notifyListLoaded.addItem(handler);
		}
		
		/**
		 * Add a handler to the list of list load failed handlers.
		 * Functions in this list will be notified when the list of objects
		 * is failed to load from server.
		 **/
		public function addListFailedHandler(failedCallback:Function):void{
			if(!_notifyListFailed.contains(failedCallback))
				_notifyListFailed.addItem(failedCallback);
		}
		
		/**
		 * Remove a handler that was previously added using #addListLoadedHandler(Function)
		 * This will cause this handler to stop receiving events when the list is reloaded
		 **/
		public function removeListLoadedHandler(handler:Function):void{
			var i:int = _notifyListLoaded.getItemIndex(handler);
			if (i != -1)
				_notifyListLoaded.removeItemAt(i);
		}
		
		/**
		 * Remove a handler that was previously added using #addListFailedHandler(Function)
		 * This will cause this handler to stop receiving events when the list is reloaded
		 **/
		public function removeListFailedHandler(handler:Function):void{
			var i:int = _notifyListFailed.getItemIndex(handler);
			if (i != -1)
				_notifyListFailed.removeItemAt(i);
		}
		
		/**
		 * Sets the base URL under which this resource is located
		 **/
		public function set baseURL(val:String):void{
			this._baseURL = val;
		}
		
		/////////////////////////////// Private Methods //////////////////////////////////
		
		/**
		 * Handle external changes of the colleciton.
		 * These changes should be reflected to the server autmatically
		 **/
		private function handleCollectionChanged(event:CollectionEvent):void{
			if (event.kind == CollectionEventKind.ADD){
				if (event.location < _collection.length-1)
					_needSorting = true;
				//Handle inserted objects
				for each (var obj:Object in event.items) {
					//check if this object was deleted before the last commit
					//This is useful when someone is reordering objects in the array be means of remove/add
					//This is found to be useful when using drag'n'drop to reorder objects
					if (_objects2bDeleted.contains(obj))
						_objects2bDeleted.removeItemAt(_objects2bDeleted.getItemIndex(obj));
					else
						_objects2bCreated.addItem(obj);
				}
			} else if (event.kind == CollectionEventKind.REMOVE) {
				if (event.oldLocation < _collection.length-1)
					_needSorting = true;
				//Handle removed objects
				for each (var removedObjectsTemp:Object in event.items) {
					//check deleting an object in cache (not saved yet)
					if (_objects2bCreated.contains(removedObjectsTemp))
						_objects2bCreated.removeItemAt(_objects2bCreated.getItemIndex(removedObjectsTemp));
					else
						_objects2bDeleted.addItem(removedObjectsTemp);
					//check deleting an object that has been updated
					for each (var objUpd:Object in _objects2bUpdated){
						if (objUpd["original 1"] === removedObjectsTemp)
							_objects2bUpdated.removeItemAt(_objects2bUpdated.getItemIndex(objUpd));
					}
				}
			} else if (event.kind == CollectionEventKind.UPDATE) {
				//Handle updated objects
				for each (var updatedObjectsTemp:Object in event.items) {
					if (updatedObjectsTemp is PropertyChangeEvent) {

						//check updating an object in cache (not saved yet)
						//check updating an object that is set for deletion
						//The last check! for a weired reason, the list fires a property change event with (oldValue=newValue=null) after an edit
						if (!_objects2bCreated.contains(updatedObjectsTemp.source) && !_objects2bDeleted.contains(updatedObjectsTemp.source) &&
								updatedObjectsTemp.newValue != updatedObjectsTemp.oldValue && _converter.isDBProperty(updatedObjectsTemp.property)){

							//check updating an object that has already been updated in cache
							var remoteObj:Object=null;
							for each (var temp:Object in _objects2bUpdated){
								if (temp["original 1"] == updatedObjectsTemp.source) {
									remoteObj = temp;
									//break;
								}
							}

							//This object is updated for the first time, add it to the "to be updated" list
							if (remoteObj == null){
								remoteObj = new Object();
								remoteObj["original 1"] = updatedObjectsTemp.source;
								_objects2bUpdated.addItem(remoteObj);
							}

							remoteObj[rParam(updatedObjectsTemp.property)] = updatedObjectsTemp.newValue;
							trace(updatedObjectsTemp.property + ' => ' +updatedObjectsTemp.newValue);
						}
					}
				}
			}
			if (autoSave&& (_objects2bCreated.length + _objects2bUpdated.length + _objects2bDeleted.length) > 0)
				commit();
		}
		
		/**
		 * This is called after the result of each http request 
		 **/ 
		private function checkCommitComplete():void{
			if (!_commitInProgress)
				return;
			if (_objectsBeingCreated.length+_objectsBeingDeleted.length+_objectsBeingUpdated.length > 0)
				return;
			//sort items
			if (_needSorting && sortable){
				reorder(function(event:ResultEvent):void{_needSorting=false});
				//this will cause this function to be fired again when sorting is completed
			} else{
				// Call all the associated call back functions with current commit
				for each (var callback:Function in _commitCallback){
					callback();
				}
				_commitCallback.removeAll();
				_commitInProgress = false;
			}
		}
		
		/**
		 * Handle a new object created
		 **/
		private function handleObjectCreated(event:ResultEvent):void{
			var obj:Object = _token2Object[event.token];
			obj.id = event.result[_resourceSingleName].id;
			//remove from hashtable
			delete _token2Object[event.token];
			if (afterCreateHandler != null)
				afterCreateHandler(obj);
			var i:int = _objectsBeingCreated.getItemIndex(obj);
			if (i != -1) {
				_objectsBeingCreated.removeItemAt(i);
			} else {
				trace('Found a token without an associated object in the correct array:'+event.token+' '+obj);
			}
			refreshCollection(event);
		}
		
		/**
		 * Returns a paramter name that is friendly with rails application
		 **/
		private function rParam(name:String):String{
			var i:int;
			while ((i=name.search("[A-Z]")) != -1){
				name = name.substr(0, i) + "_" + name.charAt(i).toLowerCase() + name.substr(i+1, name.length - i + 1);
			}
			return _resourceSingleName+"["+name+"]";
		}
		
		/**
		 * If there is a registered result handler this function fires it
		 **/
		private function fireResultHandler(event:ResultEvent):void {
			//call any registered result handler
			if (_defaultResultHandlers[event.token]){
				_defaultResultHandlers[event.token](event);
				delete _defaultResultHandlers[event.token];
			}
			//remove any registered fault handler
			if (_defaultFaultHandlers[event.token]){
				delete _defaultFaultHandlers[event.token];
			}
			checkCommitComplete();
		}
		
		/**
		 * If there is a registered fault handler this function fires it
		 **/
		private function fireFaultHandler(event:FaultEvent):void {
			//call any registered fault handler
			if (_defaultFaultHandlers[event.token]){
				_defaultFaultHandlers[event.token](event);
				delete _defaultFaultHandlers[event.token];
			}
			//remove any registered result handler
			if (_defaultResultHandlers[event.token]){
				delete _defaultResultHandlers[event.token];
			}
			// Move any associated object with this request back to the objects2bxxx lists.
			// This will make the application tried to save them again to the server.
			var obj:Object = _token2Object[event.token];
			if (_objectsBeingCreated.contains(obj)){
				_objectsBeingCreated.removeItemAt(_objectsBeingCreated.getItemIndex(obj));
				_objects2bCreated.addItem(obj);
			} else if (_objectsBeingUpdated.contains(obj)){
				_objectsBeingUpdated.removeItemAt(_objectsBeingUpdated.getItemIndex(obj));
				_objects2bUpdated.addItem(obj);
			} else if (_objectsBeingDeleted.contains(obj)){
				_objectsBeingDeleted.removeItemAt(_objectsBeingDeleted.getItemIndex(obj));
				_objects2bDeleted.addItem(obj);
			}
			checkCommitComplete();
		}
		
		/**
		 * Asks the server for a list of resources of the registered type.
		 * URL: <base_url>/<resource>s.xml
		 * METHOD: get
		 **/
		private function list(resultHandler:Function=null, faultHandler:Function=null):AsyncToken {
			// replace all '-' in resource name with '_'
			_httpService.url= _baseURL+_resourceName.replace(/-/g, '_')+".xml";			
			_httpService.method  = "get";

			var token:AsyncToken = _httpService.send();
			if (resultHandler != null)
				_defaultResultHandlers[token] = resultHandler;
			if (faultHandler != null)
				_defaultFaultHandlers[token] = faultHandler;
			return token;
		}
		
		/**
		 * Handle a list of resources retrieved by the server
		 **/
		private function handleList(event:ResultEvent):void {
			_collection.removeEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChanged);

			//retrieve a list of ids
			var listOfIds:ArrayCollection = new ArrayCollection();
			for each (var oldObj:Object in _collection)
				listOfIds.addItem(oldObj.id);

			//add items retrieved from server
			if (event.result.hasOwnProperty(_resourceName)){
				var root:Object = event.result[_resourceName];
				var objects:Object = root[_resourceSingleName];
				for each (var remoteObj:Object in toArrayCollection(objects)) {
					if (remoteObj != null && !listOfIds.contains(remoteObj.id.valueOf())){
						// This object is new. We need to create it
						var localObject:Object = _converter.remoteToLocal(remoteObj);
						//the previous line may have added this item already
						if (!_collection.contains(localObject))
							_collection.addItem(localObject);
					} else {
						// Search for the item with the given id and update it instead of created a new one
						for each (var tempObject:Object in _collection) {
							if (tempObject.id == remoteObj.id.valueOf()){
								_converter.remoteToLocal(remoteObj, tempObject);
							}
						}
						refreshCollection(null);
					}
				}
			}
			
			_collection.addEventListener(CollectionEvent.COLLECTION_CHANGE, handleCollectionChanged);
						
			for each (var handler:Function in _notifyListLoaded)
				handler();
		}
		
		/**
		 * handle a list load failuer
		 */
		private function handleListFailure(event:FaultEvent):void{
			for each (var handler:Function in _notifyListFailed){
				handler(event);
				//removeListFailedHandler(handler);
			}
		}
		 

		/**
		 * Creates a new resource
		 * URL: <base_url>/<resource>s.xml
		 * METHOD: post
		 **/
		private function createItem(data:Object, resultHandler:Function=null, faultHandler:Function=null):AsyncToken {
			_httpService.url= _baseURL+_resourceName.replace(/-/g, '_')+".xml"
			_httpService.method = "post";
			var token:AsyncToken = _httpService.send(data);
			if (resultHandler != null)
				_defaultResultHandlers[token] = resultHandler;
			if (faultHandler != null)
				_defaultFaultHandlers[token] = faultHandler;
			return token;
		}

		/**
		 * Delete the resource with the given id from the database
		 **/
		private function deleteItem(id:Number, resultHandler:Function=null, faultHandler:Function=null):AsyncToken {
			_httpService.url = _baseURL+_resourceName.replace(/-/g, '_')+"/"+id+".xml";
			_httpService.method = "post";
			//you have to send some parameters or else method will be changed to GET
			var dummyData:Object = new Object;
			dummyData["_method"] = "delete";
			var token:AsyncToken = _httpService.send(dummyData);
			if (resultHandler != null)
				_defaultResultHandlers[token] = resultHandler;
			if (faultHandler != null)
				_defaultFaultHandlers[token] = faultHandler;
			return token;
		}
		
		/**
		 * When an object is deleted, remove it from the "to be deleted" list
		 **/
		private function handleObjectDeleted(event:ResultEvent):void{
			var obj:Object = _token2Object[event.token];
			//remove from hashtable
			delete _token2Object[event.token];
			var i:int = _objectsBeingDeleted.getItemIndex(obj);
			if (i != -1)
				_objectsBeingDeleted.removeItemAt(i);
			else
				trace('Found a token without an associated object in the correct array:'+event.token+' '+obj);
		}
		
		/**
		 * Updates the given resource in the database
		 **/
		public function update(remoteObj:Object,resultHandler:Function=null, faultHandler:Function=null):AsyncToken {
			_httpService.url = _baseURL+_resourceName.replace(/-/g, '_')+"/"+remoteObj["original 1"].id+".xml";
			_httpService.method = "post"
			remoteObj["_method"]="put";
			var token:AsyncToken = _httpService.send(remoteObj);
			if (resultHandler != null)
				_defaultResultHandlers[token] = resultHandler;
			if (faultHandler != null)
				_defaultFaultHandlers[token] = faultHandler;
			return token;
		}
		
		/**
		 * Upon update of an object, remove it from the "to be updated" list
		 **/
		private function handleObjectUpdated(event:ResultEvent):void{
			var obj:Object = _token2Object[event.token];
			//remove from hashtable
			delete _token2Object[event.token];
			var i:int = _objectsBeingUpdated.getItemIndex(obj);
			if (i != -1)
				_objectsBeingUpdated.removeItemAt(i);
			else
				trace('Found a token without an associated object in the correct array:'+event.token+' '+obj);
			refreshCollection(event);
		}
		
		/**
		 * Requests the server to reorder items in a new order
		 * The back end table will contain a position column which will contain the new order, 
		 * also there will be a sort action to reorder the entries on the position column.
		 **/
		private function reorder(resultHandler:Function=null, faultHandler:Function=null):AsyncToken {
			var sort_order:String = "";
			for each (var item:Object in _collection){
				sort_order += item.id + ",";
			}
			//remove the last comma
			sort_order = sort_order.substr(0, sort_order.length-1);
			_httpService.url = _baseURL+_resourceName.replace(/-/g, '_')+"/sort.xml"
			_httpService.method = "post";
			var params:Object = new Object();
			params["_method"] = "put";
			params["sort_order"] = sort_order;
			var token:AsyncToken = _httpService.send(params);
			if (resultHandler != null)
				_defaultResultHandlers[token] = resultHandler;
			if (faultHandler != null)
				_defaultFaultHandlers[token] = faultHandler;
			return token;
		}

		/**
		 * When update is complete this function refreshes the collection to show changes to the user
		 **/
		private function refreshCollection(event:ResultEvent):void{
			_collection.refresh();
		}

		
		/**
		 * If the passed parameter is ArrayCollection it is returned as is.
		 * Otherwise, a new array collection is created and the given parameter is its only item. 
		 * @param obj
		 * @return 
		 * 
		 */		
		private static function toArrayCollection(obj:Object):ArrayCollection {
			var returnVal:ArrayCollection;
			if (obj is ArrayCollection){
				returnVal = ArrayCollection(obj);
			} else {
				returnVal = new ArrayCollection()
				returnVal.addItem(obj);
			}
			return returnVal;
		}
		
	
	}
}
